// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`define ADDR_DeviceType               0   //inported from top level
`define ADDR_SystemReset              2   //8'h02
`define ADDR_SupportMode              4   //inported form top level
`define ADDR_DeviceAddr               6   //inported form top level
`define ADDR_CurrentWrImageCrc        8   //8'h08, WUL
`define ADDR_ConfigStatus             50  //8'h32
`define ADDR_FlashStatus              52  //8'h34
`define ADDR_Status                   54  //8'h36
`define ADDR_LockReg                  56  //8'h38

`define ADDR_FlashOpHAddr            122  //8'h7A
`define ADDR_FlashOpLAddr            124  //8'h7C
`define ADDR_FlashOpLen              126  //8'h7E
`define ADDR_Cmd                     128  //8'h80
`define ADDR_IPRevision              130  //8'h82
`define ADDR_FWRevision              132  //8'h84 exposed to Flash_Loader block level.
`define ADDR_PrimaryErrSts           134  //8'h86

`define MAX_ADDR                     134  //Max address from list above

module RSU_CSR
    #(
        parameter FwRevision  = 16'h10CF, 
        parameter DeviceAddr  = 8'hAC,    
        parameter DeviceType  = 16'h11AD, 
        parameter SupportMode = 8'h05    
    )
  (
    input           Clock,             //FPGA Primary Synchronous Clock
    input           Reset,             //Synchronous Reset
    //avmm intf connect to I2C slave
    input   [15: 0] avmm_address,      //Write/Read Address for writing to registers
    input   [15: 0] avmm_writedata,    //Write Data for writing to registers
    input           avmm_write,        //Write Enable for marking Address and Data as valid to write
    input           avmm_read, 
    output  reg     avmm_readvalid,
    output  [15: 0] avmm_readdata,     //Read Data for reading from registers
    //connect to internal flash 
    output  [ 6: 0] FlashMemWrAddress, //Write Address for writing to storage RAM for flash writes
    output  [15: 0] FlashMemWrData,    //Write Data for writing to storage RAM for flash writes
    output          FlashMemWrEn,      //Write Enable for marking Address and Data as valid to write
    output  [31: 0] FlashOpAddr,       //Address for flash write operation
    output  [ 5: 0] FlashOpLen,        //Number of 32-bit words to do in write operation
    output          FlashOpWr,         //Starts a flash write if unlocked and ready
    output          FlashOpEr,         //Starts a flash erase if unlocked and ready
    input           FlashCmdAck,       //Acknowledges flash command to clear lock and command bits
    input           FlashBusy,         //Flash operation is currently under way
    input           FlashError,        //An error has occurred with the flash command                   
    input   [15: 0] FlashStatusVal,
    //connect to higher level                       
    input           LockPin,
	 input           I2C_CRC_Error,
    //connect to RSU HIP
    input           ApplicationError,  //Indicates an error occurred loading application image
    input   [ 3: 0] ConfigCurrent,
    output reg      ReloadApplication0, //reload image0
    output reg      ReloadApplication  //Initiate a Full reload of FPGA Application image                                                                                    
  );
    
                                                          
  //Internal Wires
  wire    [ 7: 0] Address;                  //Avalon Slave Address, renamed for simplicity
  wire            WriteEn;                  //Avalon Slave Write Enable, renamed for simplicity
  wire    [15: 0] WriteData;                //Avalon Slave Write Data, renamed for simplicity
  wire            NextReloadFPGA;           //Next value for ReloadFPGA
  wire            NextReloadFPGA0;
  wire            StickyI2cError;           //Sticky warning for status

  //Internal Registers



  reg     [15: 0] RegFile [`MAX_ADDR:0];    //Registers - 16 bit x MAX_ADDR
  reg     [23: 0] ResetDelayReg;            //Delay register to wait before reloading
  reg     [23: 0] ResetDelayReg0;  
  reg     [24: 0] StatusClrCnt;             //Startup timer to ignore certain errors and warnings
  reg             LockReg;
  reg             avmm_read_d1;
  reg             avmm_read_d2;
  
  //Use simple names for clarity in module - this module only uses the lower 8 bits
  assign Address    = avmm_address[7:0];
  assign WriteEn    = (avmm_write & (avmm_address[15:8] == 8'b0));
  assign WriteData  = avmm_writedata;
  
  //Data instead goes to flash if address is correct
  assign FlashMemWrAddress = avmm_address[6:0];
  assign FlashMemWrData    = avmm_writedata;
  assign FlashMemWrEn      = (avmm_write & (avmm_address[15:8] == 8'h01));
  
  always @ (posedge Clock) begin
    if(Reset) begin
        avmm_read_d1 <= 1'b0;
        avmm_read_d2 <= 1'b0;
    end else begin
        avmm_read_d1 <= avmm_read;
        avmm_read_d2 <= avmm_read_d1;        
    end
  end
  // assert readdatavalid for one cycle if slave_cs is/was asserted
  always @ (posedge Clock) begin
      if (Reset == 1)
        avmm_readvalid <= 1'b0;
      else
        avmm_readvalid <= (~avmm_read_d2 & avmm_read_d1);
  end
    
  //Counter to delay one second before reloading FPGA
  always @(posedge Clock)
  begin
    if(Reset)
      begin
        ResetDelayReg     <= 24'b0;
        ReloadApplication <= 1'b0;
      end
    else if(NextReloadFPGA & (ResetDelayReg == 24'b0))
      begin
        ResetDelayReg     <= ResetDelayReg + 24'd1;
        ReloadApplication <= 1'b0;
      end
    else if( (ResetDelayReg >= 24'd10000000) && (!LockPin) && (!LockReg) )
      begin
        ResetDelayReg     <= ResetDelayReg;
        ReloadApplication <= 1'b1;
      end
    else if(ResetDelayReg != 24'b0)
      begin
        ResetDelayReg     <= ResetDelayReg + 24'd1;
        ReloadApplication <= 1'b0;
      end
    else
      begin
        ResetDelayReg     <= 24'b0;
        ReloadApplication <= 1'b0;
      end
  end

    //Counter to delay one second before reloading FPGA image0
  always @(posedge Clock)
  begin
    if(Reset)
      begin
        ResetDelayReg0     <= 24'b0;
        ReloadApplication0 <= 1'b0;
      end
    else if(NextReloadFPGA0 & (ResetDelayReg0 == 24'b0))
      begin
        ResetDelayReg0     <= ResetDelayReg0 + 24'd1;
        ReloadApplication0 <= 1'b0;
      end
    else if( (ResetDelayReg0 >= 24'd10000000) && (!LockPin) && (!LockReg))
      begin
        ResetDelayReg0     <= ResetDelayReg0;
        ReloadApplication0 <= 1'b1;
      end
    else if(ResetDelayReg0 != 24'b0)
      begin
        ResetDelayReg0     <= ResetDelayReg0 + 24'd1;
        ReloadApplication0 <= 1'b0;
      end
    else
      begin
        ResetDelayReg0     <= 24'b0;
        ReloadApplication0 <= 1'b0;
      end
  end

  
  //Read logic for Register File matrix - null if unsupported address to prevent wrapping
  assign avmm_readdata = (avmm_address > `MAX_ADDR) ? 16'hFFFF : RegFile[avmm_address[7:0]];
  
  /////////////////////////////////////////////////////////////////////////////
  // R/W Registers start here
  /////////////////////////////////////////////////////////////////////////////

  //Load image1
  assign NextReloadFPGA = RegFile[`ADDR_Cmd] == 16'h0002;
  
    //Load image0
  assign NextReloadFPGA0 = RegFile[`ADDR_Cmd] == 16'h0001;
  

  //Lock Register after asserted
  always @(posedge Clock) begin
     if( Reset ) begin
        RegFile[`ADDR_LockReg] <= 16'b0;
        LockReg                <= 1'b0;
     end else if( WriteEn & (Address == 8'd`ADDR_LockReg) & (WriteData == 8'd1) ) begin
        RegFile[`ADDR_LockReg] <= 16'd1;
        LockReg                <= 1'b1;
     end else begin
        RegFile[`ADDR_LockReg] <= RegFile[`ADDR_LockReg];
        LockReg                <= LockReg;
     end
  end

    always @(posedge Clock)
  begin
    if(Reset)
      RegFile[`ADDR_Cmd] <= 16'b0;
        else if(FlashCmdAck)
        RegFile[`ADDR_Cmd] <= (RegFile[`ADDR_Cmd] & 16'hFFF3);    //Reset ADDR_Cmd when Flash oprates
    else if(WriteEn & (Address == 8'd`ADDR_Cmd))
      RegFile[`ADDR_Cmd] <= WriteData;
    else
      //Self clear, all signals are only needed for a single clock
      RegFile[`ADDR_Cmd] <= 16'b0;
  end

    //Register for Flash command
  assign FlashOpWr = (RegFile[`ADDR_Cmd]==16'h0008)&&(!LockPin)&&(!LockReg);
  assign FlashOpEr = RegFile[`ADDR_Cmd]==16'h0004&&(!LockPin)&&(!LockReg);

  
  //Register for Flash address
  assign FlashOpAddr = {RegFile[`ADDR_FlashOpHAddr],RegFile[`ADDR_FlashOpLAddr]};
  
  
  always @(posedge Clock)
  begin
    if(Reset)
      RegFile[`ADDR_FlashOpHAddr] <= 16'b0;
    else if(WriteEn & (Address == 8'd`ADDR_FlashOpHAddr))
      RegFile[`ADDR_FlashOpHAddr] <= WriteData;
    else
      RegFile[`ADDR_FlashOpHAddr] <= RegFile[`ADDR_FlashOpHAddr];
  end
  
  always @(posedge Clock)
  begin
    if(Reset)
      RegFile[`ADDR_FlashOpLAddr] <= 16'b0;
    else if(WriteEn & (Address == 8'd`ADDR_FlashOpLAddr))
      RegFile[`ADDR_FlashOpLAddr] <= WriteData;
    else
      RegFile[`ADDR_FlashOpLAddr] <= RegFile[`ADDR_FlashOpLAddr];
  end

  //Register for Flash length
  assign FlashOpLen = RegFile[`ADDR_FlashOpLen][5:0];
  always @(posedge Clock)
  begin
    if(Reset)
      RegFile[`ADDR_FlashOpLen] <= 16'b0;
    else if(WriteEn & (Address == 8'd`ADDR_FlashOpLen))
      RegFile[`ADDR_FlashOpLen] <= {10'b0, WriteData[5:0]};
    else
      RegFile[`ADDR_FlashOpLen] <= RegFile[`ADDR_FlashOpLen];
  end


  //Register for Configuration status
  always @(posedge Clock)
  begin
    if(Reset)
      RegFile[`ADDR_ConfigStatus] <= 16'b0;
    else
        RegFile[`ADDR_ConfigStatus] <= {12'b0, ConfigCurrent[3:0]};
    end
    
    //Status Register
     always @(posedge Clock)
     begin
        if(Reset)
            RegFile[`ADDR_Status] <= 16'b0;
        else
            RegFile[`ADDR_Status] <= {9'b0, 1'b0,LockReg, LockPin, FlashBusy, FlashError, 1'b0, ApplicationError};
     end
    
    //Register for Flash status read
    always @(posedge Clock)
    begin
        if(Reset)
            RegFile[`ADDR_FlashStatus] <= 16'b0;
        else
            RegFile[`ADDR_FlashStatus] <= FlashStatusVal;
    end
    
  /////////////////////////////////////////////////////////////////////////////
  // Read Only Registers start here
  /////////////////////////////////////////////////////////////////////////////
  
  //Register for hardware and firmware revision - always at address 0
  always @(posedge Clock)
  begin
    if(Reset)
      RegFile[`ADDR_IPRevision] <= 16'b0;
    else
      RegFile[`ADDR_IPRevision] <= {4'b0, 12'h123};
  end  
 

 //Register for hardware and firmware revision
  always @(posedge Clock)
  begin
    if(Reset)
      RegFile[`ADDR_FWRevision] <= 16'b0;
    else
      RegFile[`ADDR_FWRevision] <= FwRevision;
  end
 
  //Register for Device Address
  always @(posedge Clock)
  begin
    if(Reset)
      RegFile[`ADDR_DeviceAddr] <= 16'b0;
    else
      RegFile[`ADDR_DeviceAddr] <= {8'b0, DeviceAddr};
  end
  
    //Register for Device Type
  always @(posedge Clock)
  begin
    if(Reset)
      RegFile[`ADDR_DeviceType] <= 16'b0;
    else
      RegFile[`ADDR_DeviceType] <= DeviceType;
  end
  
    //Register for SupportMode
  always @(posedge Clock)
  begin
    if(Reset)
      RegFile[`ADDR_SupportMode] <= 16'b0;
    else
      RegFile[`ADDR_SupportMode] <= {8'b0, SupportMode};
  end
  
  
  //Register for Status
  always @(posedge Clock)
  begin
    if(Reset)
      begin
        RegFile[`ADDR_PrimaryErrSts][15:0] <= 16'b0;
      end
    else
      begin
        RegFile[`ADDR_PrimaryErrSts][    0] <= ApplicationError;
        RegFile[`ADDR_PrimaryErrSts][    1] <= I2C_CRC_Error;
        RegFile[`ADDR_PrimaryErrSts][    2] <= FlashError;
        RegFile[`ADDR_PrimaryErrSts][    3] <= FlashBusy;
        RegFile[`ADDR_PrimaryErrSts][15: 4] <= 12'b0;
      end
  end
  
    //Register for CrcVal
  always @(posedge Clock)
  begin
    if(Reset)
      begin
          RegFile[`ADDR_CurrentWrImageCrc][15:0] <= 16'hFF;
      end
    else
      begin
          RegFile[`ADDR_CurrentWrImageCrc]<= 'h0;
      end
  end
  
  //Register Unused Region [1]
  always @(posedge Clock) begin
    if(Reset)
        RegFile[`ADDR_SystemReset] <= 16'b0;
  end
  
   //Register Unused Region [49:5]
  integer i;
  always @(posedge Clock) begin
    if(Reset) begin
        for ( i=9; i<=49; i=i+1 ) begin
            RegFile[i] <= 16'b0;
        end
    end
  end
  
  //Register Unused Region [122:54]
  integer j;
  always @(posedge Clock) begin
    if(Reset) begin
        for (j=57; j<=121; j=j+1) begin
            RegFile[j] <= 16'b0;
        end
    end
  end

endmodule
